﻿// 此文件用以实现打印功能，来自 SharpDevelop 项目。

using System;
using System.Drawing.Printing;
using System.Printing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Forms;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    public static class Printing
    {
        private static PageSettings m_PageSettings;
        private static PrintQueue m_PrintQueue = LocalPrintServer.GetDefaultPrintQueue();
        private static PrintTicket m_PrintTicket = m_PrintQueue.DefaultPrintTicket;
        private static string m_DocumentTitle;

        /// <summary>
        /// Invokes a Windows.Forms.PrintPreviewDialog.
        /// </summary>
        public static bool PageSetupDialog(this TextEditor textEditor)
        {
            return PageSetupDialog();
        }

        /// <summary>
        /// Invokes a Windows.Forms.PrintPreviewDialog.
        /// </summary>
        public static bool PageSetupDialog()
        {
            InitPageSettings();

            if (m_PrintTicket.PageOrientation == PageOrientation.Landscape)
                m_PageSettings.Landscape = true;
            else
                m_PageSettings.Landscape = false;

            PageSetupDialog setup = new PageSetupDialog();
            setup.EnableMetric = true;
            setup.PageSettings = m_PageSettings;

            if (setup.ShowDialog() == DialogResult.OK)
            {
                m_PageSettings = setup.PageSettings;
                m_PrintTicket.PageOrientation = (m_PageSettings.Landscape ? PageOrientation.Landscape : PageOrientation.Portrait);
                m_PrintTicket.PageMediaSize = ConvertPaperSizeToMediaSize(m_PageSettings.PaperSize);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Invokes a PrintEngine.PrintPreviewDialog to print preview the TextEditor.Document.
        /// </summary>
        public static void PrintPreviewDialog(this TextEditor textEditor)
        {
            PrintPreviewDialog(textEditor, "");
        }

        /// <summary>
        /// Invokes a PrintEngine.PrintPreviewDialog to print preview the TextEditor.Document with specified title.
        /// </summary>
        public static void PrintPreviewDialog(this TextEditor textEditor, string title)
        {
            try
            {
                m_DocumentTitle = title;

                InitPageSettings();

                PrintPreviewDialog printPreview = new PrintPreviewDialog();
                printPreview.DocumentViewer.FitToMaxPagesAcross(1);

                printPreview.DocumentViewer.PrintQueue = m_PrintQueue;

                if (m_PageSettings.Landscape)
                    m_PrintTicket.PageOrientation = PageOrientation.Landscape;

                printPreview.DocumentViewer.PrintTicket = m_PrintTicket;
                printPreview.DocumentViewer.PrintQueue.DefaultPrintTicket.PageOrientation = m_PrintTicket.PageOrientation;

                printPreview.LoadDocument(CreateDocumentPaginatorToPrint(textEditor));

                // this is stupid, but must be done to view a whole page:
                DocumentViewer.FitToMaxPagesAcrossCommand.Execute("1", printPreview.DocumentViewer);

                // we never get a return code 'true', since we keep the DocumentViewer open, until user closes the window
                printPreview.ShowDialog();

                m_PrintQueue = printPreview.DocumentViewer.PrintQueue;
                m_PrintTicket = printPreview.DocumentViewer.PrintTicket;
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// Invokes a System.Windows.Controls.PrintDialog to print the TextEditor.Document.
        /// </summary>
        public static void PrintDialog(this TextEditor textEditor)
        {
            PrintDialog(textEditor, "");
        }

        /// <summary>
        /// Invokes a System.Windows.Controls.PrintDialog to print the TextEditor.Document with specified title.
        /// </summary>
        public static void PrintDialog(this TextEditor textEditor, string title)
        {
            try
            {
                m_DocumentTitle = title;

                InitPageSettings();

                System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog();

                printDialog.PrintQueue = m_PrintQueue;

                if (m_PageSettings.Landscape)
                    m_PrintTicket.PageOrientation = PageOrientation.Landscape;

                printDialog.PrintTicket = m_PrintTicket;
                printDialog.PrintQueue.DefaultPrintTicket.PageOrientation = m_PrintTicket.PageOrientation;

                if (printDialog.ShowDialog() == true)
                {
                    m_PrintQueue = printDialog.PrintQueue;
                    m_PrintTicket = printDialog.PrintTicket;

                    printDialog.PrintDocument(CreateDocumentPaginatorToPrint(textEditor), "PrintJob");
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// Prints the the TextEditor.Document to the current printer (no dialogs).
        /// </summary>
        public static void PrintDirect(this TextEditor textEditor)
        {
            PrintDirect(textEditor, "");
        }

        /// <summary>
        /// Prints the the TextEditor.Document to the current printer (no dialogs) with specified title.
        /// </summary>
        public static void PrintDirect(this TextEditor textEditor, string title)
        {
            try
            {
                m_DocumentTitle = title;
                InitPageSettings();
                System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog();
                printDialog.PrintQueue = m_PrintQueue;

                if (m_PageSettings.Landscape)
                    m_PrintTicket.PageOrientation = PageOrientation.Landscape;

                printDialog.PrintTicket = m_PrintTicket;
                printDialog.PrintQueue.DefaultPrintTicket.PageOrientation = m_PrintTicket.PageOrientation;

                printDialog.PrintDocument(CreateDocumentPaginatorToPrint(textEditor), title);
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// If not initialized, initialize a new instance of the PageSettings and sets the default margins.
        /// </summary>
        static void InitPageSettings()
        {
            if (m_PageSettings == null)
            {
                m_PageSettings = new PageSettings();
                m_PageSettings.Margins = new Margins(40, 40, 40, 40);
            }
        }

        /// <summary>
        /// Creates a DocumentPaginatorWrapper from TextEditor text to print.
        /// </summary>
        static DocumentPaginatorWrapper CreateDocumentPaginatorToPrint(TextEditor textEditor)
        {
            // this baby adds headers and footers
            IDocumentPaginatorSource dps = CreateFlowDocumentToPrint(textEditor);
            DocumentPaginatorWrapper dpw = new DocumentPaginatorWrapper(dps.DocumentPaginator, m_PageSettings, m_PrintTicket, textEditor.FontFamily);
            dpw.Title = m_DocumentTitle;

            return dpw;
        }

        /// <summary>
        /// Creates a FlowDocument from TextEditor text to print.
        /// </summary>
        static FlowDocument CreateFlowDocumentToPrint(TextEditor textEditor)
        {
            // this baby has all settings to be printed or previewed in the PrintEngine.PrintPreviewDialog
            FlowDocument doc = CreateFlowDocumentForEditor(textEditor);

            doc.ColumnWidth = m_PageSettings.PrintableArea.Width;
            doc.PageHeight = (m_PageSettings.Landscape ? (int)m_PrintTicket.PageMediaSize.Width : (int)m_PrintTicket.PageMediaSize.Height);
            doc.PageWidth = (m_PageSettings.Landscape ? (int)m_PrintTicket.PageMediaSize.Height : (int)m_PrintTicket.PageMediaSize.Width);
            doc.PagePadding = ConvertPageMarginsToThickness(m_PageSettings.Margins);
            doc.FontFamily = textEditor.FontFamily;
            doc.FontSize = textEditor.FontSize;
            return doc;
        }

        /// <summary>
        /// Creates a FlowDocument from TextEditor text.
        /// </summary>
        static FlowDocument CreateFlowDocumentForEditor(TextEditor editor)
        {
            // ref.:  http://community.sharpdevelop.net/forums/t/12012.aspx

            IHighlighter highlighter = editor.TextArea.GetService(typeof(IHighlighter)) as IHighlighter;
            FlowDocument doc = new FlowDocument(ConvertTextDocumentToBlock(editor.Document, highlighter));
            return doc;
        }

        public static Block ConvertTextDocumentToBlock(IDocument document, IHighlighter highlighter)
        {
            if (document == null)
                throw new ArgumentNullException("document");
            Paragraph p = new Paragraph();
            p.TextAlignment = TextAlignment.Left;
            for (int lineNumber = 1; lineNumber <= document.LineCount; lineNumber++)
            {
                if (lineNumber > 1)
                    p.Inlines.Add(new LineBreak());
                var line = document.GetLineByNumber(lineNumber);
                if (highlighter != null)
                {
                    HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber);
                    p.Inlines.AddRange(highlightedLine.ToRichText().CreateRuns());
                }
                else
                {
                    p.Inlines.Add(document.GetText(line));
                }
            }
            return p;
        }

        /// <summary>
        /// Converts PaperSize (hundredths of an inch) to PageMediaSize (px).
        /// </summary>
        static PageMediaSize ConvertPaperSizeToMediaSize(PaperSize paperSize)
        {
            return new PageMediaSize(ConvertToPx(paperSize.Width), ConvertToPx(paperSize.Height));
        }

        /// <summary>
        /// Converts specified Margins (hundredths of an inch) to Thickness (px).
        /// </summary>
        static Thickness ConvertPageMarginsToThickness(Margins margins)
        {
            Thickness thickness = new Thickness();
            thickness.Left = ConvertToPx(margins.Left);
            thickness.Top = ConvertToPx(margins.Top);
            thickness.Right = ConvertToPx(margins.Right);
            thickness.Bottom = ConvertToPx(margins.Bottom);
            return thickness;
        }

        /// <summary>
        /// Converts specified inch (hundredths of an inch) to pixels (px).
        /// </summary>
        static double ConvertToPx(double inch)
        {
            return inch * 0.96;
        }
    }
}
